package com.example.check_in.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.example.check_in.feign.PaymentFeignClient;
import com.example.check_in.feign.RoomFeignClient;
import com.example.check_in.mapper.CheckInGuestMapper;
import com.example.check_in.mapper.CheckInMapper;
import com.example.check_in.mapper.DepositMapper;
import com.example.check_in.mapper.ScheduleMapper;
import com.example.check_in.service.CheckInService;
import com.example.check_in.util.IdWorker;
import com.example.check_in.util.YearMonthUtil;
import com.example.core.exception.HotelException;
import com.example.model.check_in.*;
import com.example.model.payment.PaymentResultVo;
import com.example.model.payment.Payment;
import com.example.model.room.Room;
import com.example.model.room_type.RoomType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.Period;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.example.model.constants.CodeConstants.*;

@Service
public class CheckInServiceImpl implements CheckInService {
    private CheckIn checkIn;

    @Autowired
    private RoomFeignClient roomFeignClient;

    @Autowired
    private CheckInMapper checkInMapper;

    @Autowired
    private ScheduleMapper scheduleMapper;

    @Autowired
    private CheckInGuestMapper checkInGuestMapper;

    @Autowired
    private DepositMapper depositMapper;

    @Autowired
    private PaymentFeignClient paymentFeignClient;

    @Autowired
    private IdWorker idWorker;

    @Override
    public CheckIn start() {
        if (this.checkIn != null) {
            throw new HotelException(DOUBLE_ORDER ,"已经开始一个订单，请先处理旧订单");
        }
        this.checkIn = new CheckIn(idWorker.getId("order"));
        return this.checkIn;
    }

    @Override
    @Transactional
    public CheckIn enterInfo(OrderDo order) {
        Room room = BeanUtil.toBean(roomFeignClient.get(order.getRoomNo()).getData(), Room.class);
        System.out.println(order.toString());
        System.out.println(order.getCheckInGuests());
//        if (room.getStatus() != 0) {
//            throw new HotelException(ROOM_NOT_AVAILABLE, "房间已被占用");
//        }
        order.getCheckInGuests().forEach(checkInGuest -> {
            checkInGuest.setCheckInId(checkIn.getId());
        });
        checkIn.setGuests(order.getCheckInGuests());
        checkIn.setRoom(room);
        checkIn.setStatus(1);
        checkIn.setScheduleFlag(0);
        checkIn.setAmount(room.getRoomType().getPriceRule().getPrice()
                .multiply(BigDecimal.valueOf(order.getCheckInDay())));
        LocalDateTime checkInTime = LocalDateTime.now().withHour(12).withMinute(0).withSecond(0).withNano(0);
        checkIn.setCheckInDate(checkInTime);
        checkIn.setCheckOutDate(checkInTime.plusDays(order.getCheckInDay()));

        checkInMapper.enterInfo(checkIn);
        checkInGuestMapper.insertBatch(checkIn.getGuests());

        room.setStatus(1);
        roomFeignClient.update(room);
        return checkIn;
    }


    //有旧订单时加载旧订单
    @Override
    public CheckIn loadCheckIn() {
        return checkIn;
    }
    @Override
    @Transactional
    public PaymentResultVo makePayment(String paymentType, BigDecimal pay) {
        Payment payment = new Payment();
        payment.setPaymentId(idWorker.getId("payment"));
        payment.setPaymentType(paymentType);
        checkIn.setPayment(payment);
        PaymentResultVo paymentResultVo =
                BeanUtil.toBean(paymentFeignClient.makePayment(payment, checkIn.getAmount(), pay).getData(), PaymentResultVo.class);
        checkInMapper.makePayment(checkIn);
        try {
            return paymentResultVo;
        }
        finally {
            this.checkIn = null;  //清空销售记录
        }
    }

    @Override
    public PaymentResultVo delayPay(Long checkInId, String paymentType, BigDecimal pay) {
        this.checkIn = checkInMapper.get(checkInId);
        return makePayment(paymentType, pay);
    }


    @Override
    public void clear() {
        this.checkIn = null;
    }

    @Override
    public List<CheckIn> list(CheckIn checkIn) {
        List<CheckIn> collect = checkInMapper.list(checkIn).stream()
                .peek(checkIn1 ->
                        checkIn1.setRoom(BeanUtil.toBean(
                                roomFeignClient.get(checkIn1.getRoomNo()).getData(), Room.class)))
                .peek(checkIn1 -> {
                    if (checkIn1.getPaymentId() != null) {
                        checkIn1.setPayment(BeanUtil.toBean(
                                paymentFeignClient.get(checkIn1.getPaymentId()).getData(), Payment.class));
                    }
                })
                .collect(Collectors.toList());
        return collect;
    }

    @Override
    public int delete(Long id) {
        return checkInMapper.delete(id);
    }

    @Override
    public CheckIn get(Long id) {
        CheckIn check = checkInMapper.get(id);
        check.setRoom(BeanUtil.toBean(roomFeignClient.get(check.getRoomNo()).getData(), Room.class));
        check.setPayment(BeanUtil.toBean(paymentFeignClient.get(check.getPaymentId()).getData(), Payment.class));
        CheckInGuest checkInGuest = new CheckInGuest();
        checkInGuest.setCheckInId(id);
        check.setGuests(checkInGuestMapper.list(checkInGuest));
        return check;
    }

    @Override
    public void checkOut(Long checkInId){
        CheckIn check = get(checkInId);
        if (check.getScheduleFlag().equals(1)) {
            ScheduleCheckIn scheduleCheckIn = scheduleMapper.get(checkInId);
            Deposit deposit = scheduleCheckIn.getDeposit();
            paymentFeignClient.refund(deposit.getPaymentId(), deposit.getDepositAmount());
            deposit.setIsRefund(1);
            depositMapper.update(deposit);
        }
        Room room = check.getRoom();
        room.setStatus(0);
        roomFeignClient.update(room);
    }

    @Override
    public List<CheckIn> checkOutList() {
        return checkInMapper.checkOutList().stream()
                .peek(checkIn -> checkIn.setRoom(BeanUtil.toBean(
                        roomFeignClient.get(checkIn.getRoomNo()).getData(), Room.class)))
                .toList();
    }

    @Override
    public Boolean isEnable(Long roomId, String checkInDate) {
        return checkInMapper.isEnable(roomId, checkInDate).isEmpty();
    }





    @Override
    public CheckInData listData(String type) {
        LocalDateTime dateTime = LocalDateTime.now();
        int calculatedDay = dateTime.getDayOfWeek().getValue();
        LocalDateTime dayAgo = dateTime.minusDays(calculatedDay-1);
        int fullDays = 7;
        switch (type) {
            case "nowWeek": break;
            case "lastWeek": {
                dateTime = dateTime.minusDays(calculatedDay);
                dayAgo = dateTime.minusDays(6);
                calculatedDay = 7;
                break;
            }
            case "nowMonth": {
                fullDays = YearMonthUtil.getDaysForMonth(dateTime.getYear(),dateTime.getMonthValue());
                calculatedDay = dateTime.getDayOfMonth();
                dayAgo = dateTime.minusDays(calculatedDay-1);
                break;
            }
            case "lastMonth": {
                dateTime = dateTime.minusDays(dateTime.getDayOfMonth());
                calculatedDay = YearMonthUtil.getDaysForMonth(dateTime.getYear(),dateTime.getMonthValue());
                dayAgo = dateTime.minusDays(calculatedDay);
                break;
            }
        }
        CheckIn check = new CheckIn();
        check.setCheckInDate(dayAgo);
        check.setCheckOutDate(dateTime);
        check.setStatus(1);
        List<CheckIn> checkInList = list(check);

        //基本数据处理
        CheckInData checkInData = new CheckInData(calculatedDay);
        for (int i = calculatedDay-1; i >=0; i--) {
            LocalDateTime todayNoon = dateTime.minusDays(i).withHour(12).withMinute(0).withSecond(0).withNano(0);
            CheckInData inData = new CheckInData();
            BigDecimal dayAmount = BigDecimal.ZERO;
            List<CheckIn> list = checkInList.stream().filter(checkIn -> checkIn.getCheckInDate().equals(todayNoon)).toList();
            for (CheckIn checkIn : list) {
                dayAmount = dayAmount.add(checkIn.getAmount());
            }
            inData.setAmount(dayAmount);
            inData.setTotal(list.size());
            inData.setDateTime(todayNoon);
            checkInData.add(inData);
        }
        checkInData.calculate();
        //填补数据条目，保证数据数量与天数对应
        for (int i = fullDays; i - checkInData.getList().size() > 0;) {
            checkInData.add(new CheckInData());
        }

        // 房型处理
        Map<String, Long> roomTypeCountMap = checkInList.stream()
                .map(CheckIn::getRoom)
                .map(Room::getRoomType)
                .map(RoomType::getRoomTypeName)
                .collect(Collectors.groupingBy(
                        roomTypeName -> roomTypeName,
                        Collectors.counting()));
        checkInData.setFavoriteRoomTypeName(
                roomTypeCountMap.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey).orElse("暂无房型数据"));

        return checkInData;
    }


    @Override
    public CheckInData listDataByRoomType(String type, String typeName) {
        LocalDateTime dateTime = LocalDateTime.now();
        int calculatedDay = dateTime.getDayOfWeek().getValue();
        LocalDateTime dayAgo = dateTime.minusDays(calculatedDay-1);
        int fullDays = 7;
        switch (type) {
            case "nowWeek": break;
            case "lastWeek": {
                dateTime = dateTime.minusDays(calculatedDay);
                dayAgo = dateTime.minusDays(6);
                calculatedDay = 7;
                break;
            }
            case "nowMonth": {
                fullDays = YearMonthUtil.getDaysForMonth(dateTime.getYear(),dateTime.getMonthValue());
                calculatedDay = dateTime.getDayOfMonth();
                dayAgo = dateTime.minusDays(calculatedDay-1);
                break;
            }
            case "lastMonth": {
                dateTime = dateTime.minusDays(dateTime.getDayOfMonth());
                calculatedDay = YearMonthUtil.getDaysForMonth(dateTime.getYear(),dateTime.getMonthValue());
                dayAgo = dateTime.minusDays(calculatedDay);
                break;
            }
        }
        CheckIn check = new CheckIn();
        check.setCheckInDate(dayAgo);
        check.setCheckOutDate(dateTime);
        check.setStatus(1);
        List<CheckIn> checkInList = list(check);

        // 根据房型过滤数据
        if (typeName != null && !typeName.trim().isEmpty()) {
            checkInList = checkInList.stream()
                    .filter(checkIn -> {
                        Room room = checkIn.getRoom();
                        return room != null
                                && room.getRoomType() != null
                                && typeName.equals(room.getRoomType().getRoomTypeName());
                    })
                    .collect(Collectors.toList());
        }

        CheckInData checkInData = new CheckInData(calculatedDay);
        for (int i = calculatedDay-1; i >=0; i--) {
            LocalDateTime todayNoon = dateTime.minusDays(i).withHour(12).withMinute(0).withSecond(0).withNano(0);
            CheckInData inData = new CheckInData();
            BigDecimal dayAmount = BigDecimal.ZERO;
            List<CheckIn> list = checkInList.stream().filter(checkIn -> checkIn.getCheckInDate().equals(todayNoon)).toList();
            for (CheckIn checkIn : list) {
                dayAmount = dayAmount.add(checkIn.getAmount());
            }
            inData.setAmount(dayAmount);
            inData.setTotal(list.size());
            inData.setDateTime(todayNoon);
            checkInData.add(inData);
        }
        checkInData.calculate();
        for (int i = fullDays; i - checkInData.getList().size() > 0;) {
            checkInData.add(new CheckInData());
        }

        Map<String, Long> roomTypeCountMap = checkInList.stream()
                .map(CheckIn::getRoom)
                .map(Room::getRoomType)
                .map(RoomType::getRoomTypeName)
                .collect(Collectors.groupingBy(
                        roomTypeName -> roomTypeName,
                        Collectors.counting()));
        checkInData.setFavoriteRoomTypeName(
                roomTypeCountMap.entrySet().stream()
                        .max(Map.Entry.comparingByValue())
                        .map(Map.Entry::getKey).orElse("暂无房型数据"));

        return checkInData;
    }
}
